﻿using System;
using System.Collections.Generic;
using System.Text;
using Pixysoft.Framework.XSchEmA.Entity;
using Pixysoft.Framework.XSchEmA;
using Pixysoft.Framework.XSchEmA.Comparer;

namespace Pixysoft.Framework.XSchEmA.Modifier
{
    /// <summary>
    /// 仅仅针对表结构
    /// </summary>
    class DatabaseModifier
    {
        ISchemaAspect aspect;

        ISchemaCommand command;

        public DatabaseModifier(ISchemaAspect aspect, ISchemaCommand command)
        {
            this.aspect = aspect;

            this.command = command;
        }


        public bool UpdateComment(DatabaseSchema updateFrom, DatabaseSchema updateTo)
        {
            foreach (TableSchema tableTo in updateTo.Tables)
            {
                TableSchema tableFrom = updateFrom.GetTable(tableTo.TableName);

                if (tableFrom == null)
                    continue;

                tableTo.Comment = tableFrom.Comment;

                foreach (ColumnSchema columnTo in tableTo.Columns)
                {
                    ColumnSchema columnFrom = tableFrom.GetColumn(columnTo.ColumnName);
                    if (columnFrom == null)
                        continue;
                    columnTo.Comment = columnFrom.Comment;
                }
            }

            return true;
        }

        public bool Update(DatabaseSchema updateFrom, DatabaseSchema updateTo)
        {
            //更新表

            IDbComparer comparer = new DatabaseComparer();

            IDataTableCompareResult result = comparer.CompareTable(updateFrom, updateTo);


            if (!UpdateBSuplusTables(result, updateTo))
                return false;

            //插入新建的表

            if (!UpdateASuplusTables(result, updateFrom, updateTo))
                return false;

            //更新变动的表

            if (!UpdateTables(result, updateFrom, updateTo))
                return false;


            //更新约束

            updateFrom.Refresh();

            updateTo.Refresh();

            if (!UpdateConstraint(updateFrom, updateTo))
                return false;

            return true;
        }

        private bool UpdateASuplusTables(IDataTableCompareResult result, DatabaseSchema updateFrom, DatabaseSchema updateTo)
        {
            //要预先加载和这个表相关的主键、唯一约束

            foreach (TableSchema table in result.ASuplusTables)
            {
                try
                {
                    new RealTableModifier(command, aspect).CreateTable(updateTo, updateFrom, table);
                }
                catch (Exception ex)
                {
                    LoggerHelper.Fatal("error when create table in updateto,tablename = {0}", table.TableName);
                    LoggerHelper.Fatal(ex);
                    return false;
                }
            }

            return true;
        }

        private bool UpdateBSuplusTables(IDataTableCompareResult result, DatabaseSchema updateTo)
        {
            //foreach (TableSchema table in result.BSuplusTables)
            //{
            //    try
            //    {
            //        if (!new RealTableModifier(command, aspect).DeleteTable(updateTo, table.TableName))
            //        {
            //            LoggerHelper.Error("error when delete table in updateto,tablename = {0}", table.TableName);
            //            return false;
            //        }
            //    }
            //    catch (Exception ex)
            //    {
            //        LoggerHelper.Fatal("error when delete table in updateto,tablename = {0}", table.TableName);
            //        LoggerHelper.Fatal(ex);
            //        return false;
            //    }
            //}

            return true;
        }


        private bool UpdateTables(IDataTableCompareResult result, DatabaseSchema updateFrom, DatabaseSchema updateTo)
        {
            foreach (KeyValuePair<TableSchema, TableSchema> pair in result.SameTables)
            {
                IColumnCompareResult columnresult = XSchemaManager.GetComparer().CompareColumn(pair.Key, pair.Value);

                if (!columnresult.IsSame)//如果字段不一致 直接更新
                {
                    try
                    {
                        if (UpdateColumns(columnresult, updateFrom, pair.Key, updateTo, pair.Value))
                            continue;
                    }
                    catch (Exception ex)
                    {
                        LoggerHelper.Fatal(ex);

                        return false;
                    }

                    try
                    {
                        if (!UpdateColumnsTable(updateFrom, pair.Key, updateTo, pair.Value))
                            return false;
                    }
                    catch (Exception ex)
                    {
                        LoggerHelper.Fatal(ex);

                        return false;
                    }
                }

                //TODO: 对比字段明细更新

                //if (!columnresult.IsSame)
                //{
                //    UpdateToTable(updateFrom, pair.Key, updateTo, pair.Value);
                //}
                //else if (!UpdateColumnsTable(columnresult))
                //{
                //    UpdateToTable(updateFrom, pair.Key, updateTo, pair.Value);
                //}
            }

            return true;
        }

        private bool UpdateColumns(IColumnCompareResult columnresult, DatabaseSchema updateFrom, TableSchema tableSchema, DatabaseSchema updateTo, TableSchema tableTo)
        {
            foreach (ColumnSchema acolumn in columnresult.ASuplusColumns)
            {
                //尝试在to增加column

                try
                {
                    if (new RealColumnModifier(command, aspect).AddColumn(updateTo, tableTo, acolumn))
                        continue;

                    return false;
                }
                catch (Exception ex)
                {
                    throw ex;
                }
            }

            foreach (ColumnSchema bcolumn in columnresult.BSuplusColumns)
            {
                try
                {
                    if (new RealColumnModifier(command, aspect).DeleteColumn(updateTo, bcolumn))
                        continue;

                    return false;
                }
                catch (Exception ex)
                {
                    throw ex;
                }
            }

            return true;
        }

        private bool UpdateColumnsTable(DatabaseSchema updateFrom, TableSchema tableSchema, DatabaseSchema updateTo, TableSchema tableSchema_4)
        {
            throw new NotSupportedException("not support update columns table. this should be done in updatecolumns. if is sqlite, use structclone.");

            //INoebeCommand command = manager.NoebeCommand;

            ////原表备份

            //string tablename = updateToTable.TableName;

            //string backupname = "BAK_" + Pixysoft.Security.Random.Instance.GetRandomInt(10);

            //TableSchema backuptable = updateToTable.Clone();
            //backuptable.TableName = backupname;
            //backuptable.Refresh();
            //XSchemaManager.GetSchema(databaseType).Modifier.CreateTable(updateTo, updateTo, backuptable);

            //try
            //{
            //    XSchemaManager.GetSchema(databaseType).GetRealModifier(command).CreateTable(updateTo, updateTo, backuptable);
            //}
            //catch (Exception ex)
            //{
            //    LoggerHelper.Fatal(ex);

            //    return false;
            //}

            ////导数据

            //DataTable updatetoData = manager.GetEntity(tablename);

            //try
            //{
            //    manager.Querion.Select(updatetoData);
            //}
            //catch (Exception ex)
            //{
            //    LoggerHelper.Fatal(ex);

            //    return false;
            //}

            //try
            //{
            //    command.SQL = NoebeManager.Tools.SqlHelper.GetBackupData(tablename, backupname);

            //    command.Execute();
            //}
            //catch (Exception ex)
            //{
            //    LoggerHelper.Fatal(ex);
            //    return false;
            //}

            ////删除原表

            //try
            //{
            //    XSchemaManager.GetSchema(databaseType).GetRealModifier(command).DeleteTable(updateTo, tablename);
            //}
            //catch (Exception ex)
            //{
            //    LoggerHelper.Fatal(ex);

            //    return false;
            //}

            ////新建新表

            //try
            //{
            //    //不会丢失约束和外键，如果是sqlite会在建表的时候直接创建，如果是其他，会在后面的约束对比建立

            //    XSchemaManager.GetSchema(databaseType).GetRealModifier(command).CreateTable(updateTo, updateFrom, updatefromTable);
            //}
            //catch (Exception ex)
            //{
            //    LoggerHelper.Fatal(ex);

            //    return false;
            //}

            ////导数据

            //try
            //{
            //    NoebeManager.Tools.Cloner.DataRewrite(manager, databaseType, updatetoData);
            //}
            //catch (Exception ex)
            //{
            //    LoggerHelper.Fatal(ex);

            //    return false;
            //}

            ////删备份

            //try
            //{
            //    XSchemaManager.GetSchema(databaseType).GetRealModifier(command).DeleteTable(updateTo, backuptable.TableName);
            //}
            //catch
            //{
            //}

            //return true;
        }

        private bool UpdateConstraint(DatabaseSchema updateFrom, DatabaseSchema updateTo)
        {
            IPrimarykeyCompareResult pkresult = XSchemaManager.GetComparer().ComparePrimaryKey(updateFrom, updateTo);

            //由于不支持删除表 因此会导致需要删除的表的约束仍然存在 如果是sqlite，可能会产生影响，所以sqlite不建议使用此功能

            foreach (PrimaryKeySchema pk in pkresult.BSuplusPKs)
            {
                // drop
                try
                {
                    if (!new RealColumnModifier(command, aspect).DeletePrimaryKey(updateFrom, pk))
                    {
                        LoggerHelper.Error("error when delete primarykey,tablename = {0}", pk.TableName);
                        return false;
                    }
                }
                catch (Exception ex)
                {
                    LoggerHelper.Fatal(ex);
                    LoggerHelper.Error("error when create primarykey,tablename = {0}", pk.TableName);
                    return false;
                }
            }

            foreach (PrimaryKeySchema pk in pkresult.ASuplusPKs)
            {
                //insert

                try
                {
                    new RealColumnModifier(command, aspect).CreatePrimaryKey(updateFrom, pk);
                }
                catch (Exception ex)
                {
                    LoggerHelper.Fatal(ex);
                    LoggerHelper.Error("error when create primarykey,tablename = {0}", pk.TableName);
                    return false;
                }
            }


            IUniqueCompareResult uniqueresult = XSchemaManager.GetComparer().CompareUnique(updateFrom, updateTo);

            foreach (UniqueSchema unique in uniqueresult.BSuplusUniques)
            {
                // drop
                try
                {
                    if (!new RealColumnModifier(command, aspect).DeleteUnique(updateFrom, unique))
                    {
                        LoggerHelper.Error("error when delete unique,tablename = {0}", unique.TableName);
                        return false;
                    }
                }
                catch (Exception ex)
                {
                    LoggerHelper.Fatal(ex);
                    LoggerHelper.Error("error when create unique,tablename = {0}", unique.TableName);
                    return false;
                }
            }

            foreach (UniqueSchema unique in uniqueresult.ASuplusUniques)
            {
                //insert
                try
                {
                    new RealColumnModifier(command, aspect).CreateUnique(updateFrom, unique);
                }
                catch (Exception ex)
                {
                    LoggerHelper.Fatal(ex);
                    LoggerHelper.Error("error when create unique,tablename = {0}", unique.TableName);
                    return false;
                }
            }


            IForeignkeyCompareResult fkresult = XSchemaManager.GetComparer().CompareForeignKey(updateFrom, updateTo);

            //外键先删除 后插入 防止重复外键名

            foreach (ForeignKeySchema fk in fkresult.BSuplusFKs)
            {
                // drop

                try
                {
                    if (!new RealColumnModifier(command, aspect).DeleteForeignKey(updateFrom, fk))
                    {
                        LoggerHelper.Error("error when delete foreignkey,FkTableName = {0}", fk.FkTableName);
                        return false;
                    }
                }
                catch (Exception ex)
                {
                    LoggerHelper.Fatal(ex);
                    LoggerHelper.Error("error when create unique,tablename = {0}", fk.FkTableName);
                    return false;
                }
            }

            foreach (ForeignKeySchema fk in fkresult.ASuplusFKs)
            {
                //insert
                try
                {
                    new RealColumnModifier(command, aspect).CreateForeignKey(updateFrom, fk);
                }
                catch (Exception ex)
                {
                    LoggerHelper.Fatal(ex);
                    LoggerHelper.Error("error when create foreignkey,FkTableName = {0}", fk.FkTableName);
                    return false;
                }
            }

            return true;
        }

    }
}
